package com.room.search.service;

import com.room.search.core.page.TableDataInfo;
import com.room.search.domain.*;
import com.room.search.domain.enumeration.BkRoomTypeEnum;
import com.room.search.domain.enumeration.RoomRentMethodEnum;
import com.room.search.repository.RoomCommunityRepository;
import com.room.search.repository.RoomGoodsRepository;
import com.room.search.repository.RoomSearchGoodRepository;
import com.room.search.repository.search.RoomSearchCommunitySearchRepository;
import com.room.search.repository.search.RoomSearchGoodSearchRepository;
import com.room.search.response.RoomResult;
import com.room.search.service.dto.*;
import com.room.search.service.mapper.RoomCommunityMapper;
import com.room.search.service.mapper.RoomGoodsMapper;
import com.room.search.util.PageUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import java.util.*;

/**
 * Service Implementation for managing {@link RoomSearchGood}.
 */
@Service
@Transactional
public class RoomSearchGoodService {

    private final Logger log = LoggerFactory.getLogger(RoomSearchGoodService.class);

    private static final String str1 = "—";
    private static final String str2 = "万元";
    private static final String str3 = "万元以上";
    private static final String str4 = "万元以下";
    private static final String M2 = "㎡";

    private final RoomGoodsRepository goodsRepository;
    private final RoomSearchGoodRepository searchGoodRepository;
    private final RoomSearchGoodSearchRepository searchGoodSearchRepository;
    private final RoomCommunityRepository communityRepository;
    private final RoomSearchCommunitySearchRepository communitySearchRepository;
    private final RoomGoodsMapper goodsMapper;
    private final RoomCommunityMapper communityMapper;


    public RoomSearchGoodService(RoomGoodsRepository goodsRepository,
                                 RoomSearchGoodRepository searchGoodRepository, RoomSearchGoodSearchRepository searchGoodSearchRepository,
                                 RoomCommunityRepository communityRepository,
                                 RoomSearchCommunitySearchRepository communitySearchRepository,
                                 RoomGoodsMapper goodsMapper, RoomCommunityMapper communityMapper) {
        this.goodsRepository = goodsRepository;
        this.searchGoodRepository = searchGoodRepository;
        this.searchGoodSearchRepository = searchGoodSearchRepository;
        this.communityRepository = communityRepository;
        this.communitySearchRepository = communitySearchRepository;
        this.goodsMapper = goodsMapper;
        this.communityMapper = communityMapper;
    }


    /**
     * elasticSearch处理
     */
    public TableDataInfo search(SearchDataDTO dto) {

        Set<Long> goodsIds = new HashSet<>();


        //组合条件查询
        NativeSearchQuery builder = getBoolQueryBuilder(dto);


        Page<RoomSearchGood> search = searchGoodSearchRepository.search(builder);

        search.getContent().forEach(

            e-> {
                if (e.getBk_room_type().equals(dto.getRoomType())) {
                    goodsIds.add(e.getGoods_id());
                }
            }
        );
        Pageable pageable = PageRequest.of(dto.getPageNum(), dto.getPageSize());
        List<RoomGoods> all = goodsRepository.findAll((root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (dto.getArea().getCity() != null) {
                predicates.add(criteriaBuilder.equal(root.get("city"), dto.getArea().getCity()));
            }

            if (dto.getArea().getDistrict() != null) {
                predicates.add(criteriaBuilder.like(root.get("district"), dto.getArea().getDistrict() + "%"));
            }

            if (!CollectionUtils.isEmpty(goodsIds)) {
                CriteriaBuilder.In<Object> in = criteriaBuilder.in(root.get("id"));
                for (Long goodsId : goodsIds) {
                    in.value(goodsId);
                }
                predicates.add(criteriaBuilder.and(criteriaBuilder.and(in)));
            }
            if (CollectionUtils.isEmpty(predicates)) {
                log.error("查询predicate：{}", predicates);
            }

            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));

        });
        List<RoomGoodsDTO> dtoList = goodsMapper.toDto(all);
        Page<RoomGoodsDTO> page = PageUtils.listConvertToPage1(dtoList, pageable);

        return TableDataInfo.getTableDataInfo(page);

    }


    public NativeSearchQuery getBoolQueryBuilder(SearchDataDTO dto) {

        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder queryBuilder = new BoolQueryBuilder();

        List<String> price = dto.getPrice();
        List<String> buildSize = dto.getBuildSize();

        queryBuilder.must(QueryBuilders.matchQuery("bk_room_type", dto.getRoomType()));

        if (!CollectionUtils.isEmpty(dto.getRoomStyle())) {
            dto.getRoomStyle().forEach(e ->
                queryBuilder.should(QueryBuilders.matchQuery("room_style", e))
            );
        }

        if (!CollectionUtils.isEmpty(dto.getFloor())) {
            dto.getFloor().forEach(e-> queryBuilder.should(QueryBuilders.matchQuery("floor", e)));
        }

        if (!CollectionUtils.isEmpty(dto.getFitment())) {
            dto.getFitment().forEach(e -> queryBuilder.should(QueryBuilders.matchQuery("fitment", e)));
        }


        if (!CollectionUtils.isEmpty(dto.getTowards())) {
            dto.getTowards().forEach(e -> queryBuilder.should(QueryBuilders.matchQuery("towards", e)));
        }

        if (!CollectionUtils.isEmpty(dto.getPrice())) {
            price.forEach(e->{
                Map<String, Integer> map = handlePrice(e);
                queryBuilder.should(QueryBuilders.rangeQuery("price").gte(map.get("less")).lte(map.get("greater")));
            });
        }

        if (!CollectionUtils.isEmpty(dto.getBuildSize())) {
            buildSize.forEach(e-> {
                Map<String, Integer> map = handleBuildSize(e);
                queryBuilder.should(QueryBuilders.rangeQuery("build_size").gte(map.get("lessSize")).lte(map.get("greaterSize")));
            });
        }

        queryBuilder.minimumShouldMatch(1);

        return nativeSearchQueryBuilder.withQuery(queryBuilder).build();
    }




    private Map<String, Integer> handleBuildSize(String e) {


        Map<String, Integer> map = new HashMap<>();

        if (e.equals("")) {
            return map;
        }


        if (e.contains(str1)) {

            String replace = e.replace(M2, "");
            map.put("lessSize", Integer.parseInt(replace.substring(0, replace.indexOf(str1))));
            map.put("greaterSize", Integer.parseInt(replace.substring(replace.indexOf(str1) + 1)));
        }

        if (e.contains("㎡以下")) {
            String less = e.replace("㎡以下", "");
            map.put("lessSize", 0);
            map.put("greaterSize", Integer.parseInt(less));
        }

        if (e.contains("㎡以上")) {
            map.put("lessSize", Integer.parseInt(e.replace("㎡以上", "")));
            map.put("greaterSize", 11111111);

        }

        return map;

    }

    /**
     *
     */
    public Map<String, Integer> handlePrice(String price) {
        Map<String, Integer> map = new HashMap<>();

        if (price.isEmpty()) {
            return map;
        }
        //100-200万元
        if (price.contains("万")) {
            if (price.contains(str1)) {
                String replace = price.replace(str2, "");
                map.put("less", Integer.parseInt(replace.substring(0, replace.indexOf(str1))));
                map.put("greater", Integer.parseInt(replace.substring(replace.indexOf(str1) + 1)));
            }

            if (price.contains(str4)) {
                map.put("greater", Integer.parseInt(price.replace(str4, "")));
                map.put("less", 0);

            }

            if (price.contains(str3)) {
                map.put("less", Integer.parseInt(price.replace(str3, "")));
                map.put("greater", 111111111);

            }
        } else {
            if (price.contains(str1)) {
                String replace = price.replace("元", "");
                map.put("less", Integer.parseInt(replace.substring(0, replace.indexOf(str1))));
                map.put("greater", Integer.parseInt(replace.substring(replace.indexOf(str1) + 1)));
            }

            if (price.contains("元以下")) {
                map.put("greater", Integer.parseInt(price.replace("元以下", "")));
                map.put("less", 0);

            }

            if (price.contains("元以上")) {
                map.put("less", Integer.parseInt(price.replace("元以上", "")));
                map.put("greater", 111111111);

            }
        }

        return map;
    }



    public TableDataInfo searchIndex(TypeAndLocationDTO dto) {

        Pageable pageable = PageUtils.startPage();

        List<RoomGoods> all = goodsRepository.findAll((root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            try {
                if (null != dto.getType()) {
                    predicates.add(cb.equal(root.get("bkRoomType"), BkRoomTypeEnum.valueOf(dto.getType())));
                }

                if (null != dto.getProvince()) {
                    predicates.add(cb.equal(root.get("province"), dto.getProvince()));
                }

                if (null != dto.getCity()) {
                    predicates.add(cb.equal(root.get("city"), dto.getCity()));
                }

                if (dto.getRentMethod() != null && !RoomRentMethodEnum.UNLIMITED.equals(dto.getRentMethod())) {
                    predicates.add(cb.equal(root.get("rentMethod"), dto.getRentMethod()));
                }

            } catch (Exception e) {
                log.error("error：", e);
            }

            if (CollectionUtils.isEmpty(predicates)) {
                return null;
            }

            Predicate[] p = predicates.toArray(new Predicate[0]);

            return cb.and(p);

        });
        List<RoomGoodsDTO> dtoList = goodsMapper.toDto(all);
        Page<RoomGoodsDTO> dtoPage = PageUtils.listConvertToPage1(dtoList, pageable);
        return TableDataInfo.getTableDataInfo(dtoPage);
    }

    /**
     *
     */
    public TableDataInfo searchCommunityIndex(TypeAndLocationDTO dto) {
        Pageable pageable = PageUtils.startPage();
        List<RoomCommunity> communities = communityRepository.findAllByProvinceAndCity(dto.getProvince(), dto.getCity(), null);
        List<RoomCommunityDTO> dtoList = communityMapper.toDto(communities);
        Page<RoomCommunityDTO> page = PageUtils.listConvertToPage1(dtoList, pageable);
        return TableDataInfo.getTableDataInfo(page);
    }


    public TableDataInfo searchCommunity(SearchCommunityDataDTO dto) {

        Pageable pageable = PageRequest.of(dto.getPageNum(), dto.getPageSize());
        Set<Long> communityIds = new HashSet<>();

        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();

        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        for (String average : dto.getAverage()) {
            Map<String, Integer> map = handlePrice(average);
            boolQueryBuilder.should(QueryBuilders.rangeQuery("average").lte(map.get("greater")).gte(map.get("less")));
        }

        NativeSearchQuery build = nativeSearchQueryBuilder.withQuery(boolQueryBuilder).build();
        Page<RoomSearchCommunity> search = communitySearchRepository.search(build);
        search.getContent().forEach(e-> communityIds.add(e.getCommunityId()));
        List<RoomCommunity> all = communityRepository.findAll((root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();

            if (dto.getArea().getCity() != null) {
                list.add(criteriaBuilder.equal(root.get("city"), dto.getArea().getCity()));
            }

            if (dto.getArea().getDistrict() != null) {
                list.add(criteriaBuilder.like(root.get("district"), dto.getArea().getDistrict() + "%"));
            }

            if (!CollectionUtils.isEmpty(communityIds)) {
                CriteriaBuilder.In<Object> in = criteriaBuilder.in(root.get("id"));
                for (Long communityId : communityIds) {
                    in.value(communityId);
                }
                list.add(criteriaBuilder.and(criteriaBuilder.and(in)));
            }

            return criteriaBuilder.and(list.toArray(new Predicate[0]));
        });
        List<RoomCommunityDTO> dtoList = communityMapper.toDto(all);
        Page<RoomCommunityDTO> page = PageUtils.listConvertToPage1(dtoList, pageable);
        return TableDataInfo.getTableDataInfo(page);
    }

    public RoomResult updateAll() {
        searchGoodSearchRepository.deleteAll();
        List<RoomGoods> all = goodsRepository.findAll();

        List<RoomSearchGood> searchGoods = new ArrayList<>();
        all.forEach(item->{
            RoomBasicProperty roomBasic = item.getRoomBasic();
            searchGoods.add(new RoomSearchGood()
                .setGoods_id(item.getId())
                .setBkNo(item.getBkNo())
                .setBk_room_type(item.getBkRoomType().toString())
                .setCommunityId(item.getCommunity().getId())
                .setPrice(Integer.parseInt(item.getOldprice()))
                .setTransportation(item.getTransportation())
                .setTitle(item.getTitle())
                .setArea_price(item.getAreaPrice() == null ? 0 : Integer.parseInt(item.getAreaPrice()))
                .setRent_price(!item.getBkRoomType().equals(BkRoomTypeEnum.RENT) ? 0 : Integer.parseInt(item.getOldprice()))
                .setBuild_size(Integer.parseInt(roomBasic.getBuildingSize()))
                .setRoom_style(roomBasic.getHouseType())
                .setTowards(roomBasic.getRoomTowards())
                .setFloor(roomBasic.getRoomFloor())
                .setFitment(roomBasic.getRoomFitment())

            );

        });
        searchGoodSearchRepository.saveAll(searchGoods);

        return RoomResult.success("更新成功");
    }
}
